/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.Collections.Generic;

using Microsoft.Scripting.Ast;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Generation;
using Microsoft.Scripting.Utils;

namespace Microsoft.Scripting.Actions {
    public class MetaObject {
        private readonly Expression/*!*/ _expression;
        private readonly Restrictions/*!*/ _restrictions;
        private readonly object _value;
        private readonly bool _hasValue;

        public MetaObject(Expression/*!*/ expression, Restrictions/*!*/ restrictions) {
            ContractUtils.RequiresNotNull(expression, "expression");
            ContractUtils.RequiresNotNull(restrictions, "restrictions");

            _expression = expression;
            _restrictions = restrictions;
        }

        // Only our meta objects can have value ... ? Is that good idea?
        internal MetaObject(Expression/*!*/ expression, Restrictions/*!*/ restrictions, object value)
            : this(expression, restrictions) {
            _value = value;
            _hasValue = true;
        }

        public virtual Expression/*!*/ Expression {
            get {
                return _expression;
            }
        }

        public Restrictions/*!*/ Restrictions {
            get {
                return _restrictions;
            }
        }

        public object Value {
            get {
                return _value;
            }
        }

        public bool HasValue {
            get {
                return _hasValue;
            }
        }

        public Type RuntimeType {
            get {
                if (_hasValue) {
                    if (_value != null) {
                        return _value.GetType();
                    } else {
                        return typeof(None);    // TODO: Return something else ???
                    }
                } else {
                    return null;                // TODO: Return something else ???
                }
            }
        }

        /// <summary>
        /// Checks to see if the known type is good enough for performing operations
        /// on.  This is possible if the type has a value or of the known type of the
        /// expression is a sealed type.
        /// </summary>
        internal bool NeedsDeferral {
            get {
                if (HasValue) {
                    return false;
                }

                return !CompilerHelpers.IsSealed(Expression.Type);
            }
        }

        public Type LimitType {
            get {
                return RuntimeType ?? Expression.Type;
            }
        }

        public bool IsDynamicObject {
            get {
                // We can skip _hasValue check as it implies _value == null
                return _value is IDynamicObject2;
            }
        }

        public MetaObject/*!*/ Restrict(Type type) {
            if (type == Expression.Type) {
                return this;
            }

            return RestrictImpl(type);
        }

        internal virtual MetaObject/*!*/ RestrictImpl(Type type) {
            // TODO: Should this return the always deferring MO?
            throw new InvalidOperationException("Cannot restrict");
        }

        // Operations

        public virtual MetaObject/*!*/ DoOperation(Operators op, params MetaObject/*!*/[]/*!*/ args) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ ConvertTo(Type type) {
            throw new NotImplementedException();
        }

        public virtual MetaObject GetMember(string name, bool ci) {
            throw new NotImplementedException();
        }

        public virtual MetaObject SetMember(string name, bool ci, MetaObject arg) {
            throw new NotImplementedException();
        }

        public virtual MetaObject DeleteMember(string name) {
            throw new NotImplementedException();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Call")]
        public virtual MetaObject Call(CallSignature signature, params MetaObject[] args) {
            throw new NotImplementedException();
        }

        public virtual MetaObject CreateInstance(CallSignature signature, params MetaObject[] args) {
            throw new NotImplementedException();
        }

        // Deferred operations

        public virtual MetaObject/*!*/ DeferDoOperation(Operators op, params MetaObject/*!*/[]/*!*/ args) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ DeferConvertTo(Type/*!*/ type) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ DeferGetMember(string/*!*/ name, bool ci) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ DeferSetMember(string/*!*/ name, bool ci, MetaObject/*!*/ arg) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ DeferDeleteMember(string/*!*/ name) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ DeferCall(CallSignature signature, params MetaObject/*!*/[]/*!*/ args) {
            throw new NotImplementedException();
        }

        public virtual MetaObject/*!*/ DeferCreateInstance(CallSignature signature, params MetaObject/*!*/[]/*!*/ args) {
            throw new NotImplementedException();
        }

        // Error handling

        public virtual MetaObject IfError(MetaObject mo) {
            throw new NotImplementedException();
        }

        // Internal helpers

        internal static Type[] GetTypes(MetaObject[] objects) {
            Type[] res = new Type[objects.Length];
            for (int i = 0; i < objects.Length; i++) {
                res[i] = objects[i].RuntimeType ?? objects[i].Expression.Type;
            }

            return res;
        }
    }
}
